package com.todo.sight.application.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.todo.common.common.entity.response.page.PageResult;
import com.todo.common.common.enums.BizErrorCodeEnum;
import com.todo.common.common.enums.EnableTypeEnum;
import com.todo.common.common.exception.BizException;
import com.todo.common.utils.BatchExecuteUtils;
import com.todo.common.utils.StringUtils;
import com.todo.component.redis.RedisService;
import com.todo.sight.common.base.SightKeyBuild;
import com.todo.sight.application.entity.condition.PoiConditionBO;
import com.todo.sight.application.service.SightPoiService;
import com.todo.sight.application.convert.SightServiceConvert;
import com.todo.sight.application.entity.bo.SightPoiBO;
import com.todo.sight.infrastructure.entity.condition.SightPoiCondition;
import com.todo.sight.infrastructure.entity.po.SightPoiPO;
import com.todo.sight.infrastructure.helper.es.entity.SightPoiEntity;
import com.todo.sight.infrastructure.helper.es.mapper.SightPoiEntityMapper;
import com.todo.sight.infrastructure.repository.SightPoiRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * (SightPoi)表服务实现类
 *
 * @author zhangwenxiang
 * @since 2024-03-04 00:19:48
 */
@Slf4j
@Service("sightPoiService")
public class SightPoiServiceImpl implements SightPoiService {

    public static final int MAX_QUERY_SIZE = 100;

    /**
     * 线程池执行
     */
    ThreadPoolExecutor executor = new ThreadPoolExecutor(30, 30, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1000));


    @Resource
    private SightPoiRepository sightPoiRepository;
    @Resource
    private RedisService redisService;
    @Resource
    private SightPoiEntityMapper sightPoiEntityMapper;

    @Override
    public Boolean insertSightPoi(SightPoiBO sightPoiBO) {
        if (sightPoiBO == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        if (verifyExistPoi(sightPoiBO)) {
            return true;
        }
        SightPoiPO sightPoiPO = SightServiceConvert.convertToSightPoiPO(sightPoiBO);
        Boolean inserted = sightPoiRepository.insertSightPoiPO(sightPoiPO);
        if (inserted) {
            SightPoiBO insertData = SightServiceConvert.convertToSightPoiBO(sightPoiPO);
            String key = SightKeyBuild.sightPoiDetailKey(insertData.getId());
            redisService.set(key, JSONObject.toJSONString(insertData));
            SightPoiEntity sightPoiEntity = SightServiceConvert.convertToSightPoiEntity(insertData);
            sightPoiEntityMapper.insert(sightPoiEntity);
        }
        return inserted;
    }

    /**
     * 校验poi是否存在
     *
     * @param sightPoiBO
     * @return
     */
    private boolean verifyExistPoi(SightPoiBO sightPoiBO) {
        SightPoiPO sightPoiPO = sightPoiRepository.queryPoiByCoordinate(sightPoiBO.getName(), sightPoiBO.getLon(), sightPoiBO.getLat());
        if (sightPoiPO != null) {
            log.info("SightPoiService#verifyExistPoi 校验POI是否存在，当前坐标下，存在相同名称信息，数据存在 sightPoiBO：【{}】", JSONObject.toJSONString(sightPoiBO));
            return true;
        }
        return false;
    }

    @Override
    public List<Long> batchInsertSightPoi(List<SightPoiBO> sightPoiBOList) {
        if (CollectionUtils.isEmpty(sightPoiBOList)) {
            return Lists.newArrayList();
        }
        List<List<Long>> execute = BatchExecuteUtils.execute(this::insertPoiList, sightPoiBOList, 20, executor);
        if (CollectionUtils.isEmpty(execute)) {
            return Lists.newArrayList();
        }
        List<Long> result = new ArrayList<>();
        for (List<Long> idList : execute) {
            result.addAll(idList);
        }
        return result;
    }

    /**
     * 批量插入
     *
     * @param sightPoiBOList
     * @return
     */
    private List<Long> insertPoiList(List<SightPoiBO> sightPoiBOList) {
        if (CollectionUtils.isEmpty(sightPoiBOList)) {
            return Lists.newArrayList();
        }
        List<SightPoiPO> executeList = new ArrayList<>();
        try {
            for (SightPoiBO sightPoiBO : sightPoiBOList) {
                if (verifyExistPoi(sightPoiBO)) {
                    continue;
                }
                executeList.add(SightServiceConvert.convertToSightPoiPO(sightPoiBO));
            }
            List<Long> idList = sightPoiRepository.insertSightPoiPOBatch(executeList);
            if (CollectionUtils.isNotEmpty(idList)) {
                List<SightPoiBO> collect = executeList.stream().map(SightServiceConvert::convertToSightPoiBO).collect(Collectors.toList());
                for (SightPoiBO sightPoiBO : collect) {
                    String key = SightKeyBuild.sightPoiDetailKey(sightPoiBO.getId());
                    redisService.set(key, JSONObject.toJSONString(sightPoiBO));
                }
                List<SightPoiEntity> entityList = collect.stream().map(SightServiceConvert::convertToSightPoiEntity).collect(Collectors.toList());
                sightPoiEntityMapper.insertBatch(entityList);
            }
            return idList;
        } catch (Exception ex) {
            log.error("SightPoiService#batchInsertSightPoi 批量插入POI数据异常，executeList：【{}】", JSONObject.toJSONString(executeList), ex);
        }
        return Collections.emptyList();
    }

    @Override
    public Boolean updateSightPoi(SightPoiBO sightPoiBO) {
        if (sightPoiBO == null || sightPoiBO.getId() == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        SightPoiBO alreadysightPoiBO = querySightPoiById(sightPoiBO.getId());
        if (alreadysightPoiBO == null) {
            log.warn("SightPoiService#updateSightPoi 更新POI数据异常，当前id不存在， sightPoiBO：【{}】", JSONObject.toJSONString(sightPoiBO));
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST);
        }
        Boolean updated = sightPoiRepository.updateSightPoiPOById(SightServiceConvert.convertToSightPoiPO(sightPoiBO));
        if (updated) {
            String key = SightKeyBuild.sightPoiDetailKey(sightPoiBO.getId());
            redisService.del(key);
            SightPoiEntity sightPoiEntity = SightServiceConvert.convertToSightPoiEntity(sightPoiBO);
            LambdaEsQueryWrapper<SightPoiEntity> wrapper = new LambdaEsQueryWrapper<>();
            wrapper.eq(SightPoiEntity::getMysqlId, sightPoiBO.getId());
            sightPoiEntityMapper.update(sightPoiEntity, wrapper);
        }
        return updated;
    }

    @Override
    public Boolean removeSightPoiById(Long id) {
        if (id == null || id < 0) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        SightPoiBO sightPoiBO = querySightPoiById(id);
        if (sightPoiBO == null) {
            return true;
        }
        Boolean removed = sightPoiRepository.removeSightPoiPOById(id);
        if (removed) {
            String key = SightKeyBuild.sightPoiDetailKey(sightPoiBO.getId());
            redisService.del(key);
            LambdaEsQueryWrapper<SightPoiEntity> wrapper = new LambdaEsQueryWrapper<>();
            wrapper.eq(SightPoiEntity::getMysqlId, sightPoiBO.getId());
            sightPoiEntityMapper.delete(wrapper);
        }
        return removed;
    }

    @Override
    public Boolean showSightPoiById(Long id, Integer show) {
        if (id == null || id < 0) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        SightPoiBO sightPoiBO = querySightPoiById(id);
        if (sightPoiBO == null) {
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST);
        }
        if (sightPoiBO.getIsShow().equals(EnableTypeEnum.getByType(show))) {
            return true;
        }
        sightPoiBO.setIsShow(EnableTypeEnum.getByType(show));
        Boolean updated = sightPoiRepository.updateSightPoiPOById(SightServiceConvert.convertToSightPoiPO(sightPoiBO));
        if (updated){
            // 开启状态则将数据放入ES，反之毅然
            if (show.equals(EnableTypeEnum.ENABLE.getType())){
                SightPoiEntity sightPoiEntity = SightServiceConvert.convertToSightPoiEntity(sightPoiBO);
                sightPoiEntityMapper.insert(sightPoiEntity);
            }else {
                LambdaEsQueryWrapper<SightPoiEntity> wrapper = new LambdaEsQueryWrapper<>();
                wrapper.eq(SightPoiEntity::getMysqlId, sightPoiBO.getId());
                sightPoiEntityMapper.delete(wrapper);
            }
            String key = SightKeyBuild.sightPoiDetailKey(id);
            redisService.del(key);
        }
        return updated;
    }

    @Override
    public SightPoiBO querySightPoiById(Long id) {
        if (id == null || id < 0) {
            return null;
        }
        String key = SightKeyBuild.sightPoiDetailKey(id);
        SightPoiBO sightPoiBO = JSONObject.parseObject(redisService.get(key), SightPoiBO.class);
        if (sightPoiBO != null){
            return sightPoiBO;
        }
        SightPoiBO queryData = SightServiceConvert.convertToSightPoiBO(sightPoiRepository.querySightPoiPOById(id));
        redisService.set(key, JSONObject.toJSONString(queryData));
        return queryData;
    }

    @Override
    public List<SightPoiBO> querySightPoiByName(String name, String city) {
        if (StringUtils.isBlank(name)) {
            return Collections.emptyList();
        }
        List<SightPoiPO> sightPoiPOList = sightPoiRepository.querySightPoiByName(name, city);
        return SightServiceConvert.convertToSightPoiBOList(sightPoiPOList);
    }

    @Override
    public PageResult<SightPoiBO> querySightPoiByCondition(PoiConditionBO poiConditionBO) {
        if (poiConditionBO == null) {
            return PageResult.of(Lists.newArrayList(), 0, 0, 0L);
        }
        SightPoiCondition condition = SightServiceConvert.convertToSightPoiCondition(poiConditionBO);
        PageResult<SightPoiPO> sightPoiPOPageResult = sightPoiRepository.querySightPoiByCondition(condition);
        if (sightPoiPOPageResult == null) {
            return new PageResult<>(Lists.newArrayList(), condition.getPageIndex(), condition.getPageSize(), 0L);
        }
        PageResult<SightPoiBO> result = new PageResult<>();
        result.setPageNum(condition.getPageIndex());
        result.setPageSize(condition.getPageSize());
        result.setTotal(sightPoiPOPageResult.getTotal());
        result.setCurrentIndex(sightPoiPOPageResult.getCurrentIndex());
        result.setResults(SightServiceConvert.convertToSightPoiBOList(sightPoiPOPageResult.getResults()));
        return result;
    }

    @Override
    public List<SightPoiBO> querySightPoiByIdList(List<Long> poiIdList) {
        if (CollectionUtils.isEmpty(poiIdList)) {
            return Lists.newArrayList();
        }
        if (poiIdList.size() > MAX_QUERY_SIZE) {
            throw new BizException(BizErrorCodeEnum.QUANTITY_EXPLOSION, "超出最大查询数量");
        }
        List<SightPoiPO> sightPoiPOList = sightPoiRepository.querySightPoiByIdList(poiIdList);
        return SightServiceConvert.convertToSightPoiBOList(sightPoiPOList);
    }
}
